1 Getting the Data

To get information about the job titles and salaries, I downloaded the data from the Ohio Checkbook on March 1, 2024 at 3:50 PM. The data is stored in the file data/EmployeePayroll.csv. In the code chunk below, I:

  • read the data and cleaned it up;
  • counted the number of employees in each job title; and then
  • saved the result in data/salaries_tbl.csv.

Note that there is a total of 221 unique job titles at the Ohio Department of Transportation, based on the data downloaded from the Ohio Checkbook on March 1, 2024 at 3:50 PM.

salaries = readr::read_csv("../data/EmployeePayroll.csv") |> 
  janitor::clean_names()
## Rows: 5546 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Name, Job Title, Agency, Max. Hourly Rate, Amount
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
salaries = salaries |>
  dplyr::mutate(
    max_hourly_rate = stringr::str_replace_all(max_hourly_rate, "\\$|,", "") |> as.numeric(),
    amount = stringr::str_replace_all(amount, "\\$|,", "") |> as.numeric()
  )

position_tables = table(salaries$job_title)

salaries_tbl = tibble::tibble(
  index = 1:length(position_tables),
  job_title = names(position_tables),
  count = position_tables |> as.integer()
)

readr::write_csv(salaries_tbl, "../data/salaries_tbl.csv")

DT::datatable(
  salaries_tbl |> dplyr::select(job_title, count), 
  rownames = FALSE, 
  extensions = 'Buttons',
  options = list(
    pageLength = 15, scrollX = TRUE, autoWidth = TRUE,
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf', 'print') 
    )
)

2 Projected Tasks for Each Job Title and how they would be Impacted by AI

2.1 Using OpenAI API to Generate Task Descriptions and Exposure Scores

Due to the lack of Ohio Department of Transportation (ODOT) data in the proposal stage, we will utilize the OpenAI API to perform the following four tasks for each job title in the dataset:

  1. Generate a 2-3 sentence description of a singular main task performed by the person with this title given that they work for the Ohio Department of Transportation. While they may be performing many tasks, provide the task that would be most representative of that job title.
  2. Reason step by step to decide which of the labels (E0/E1/E2/E3) from the exposure rubric you were given applies to the task’s exposure to LLM. Report. Give an explanation for which label applies and report the label that you think fits best. Do not say zero or N/A. Note that the labels correspond to:
    • E0: Used for tasks where LLMs and related tools don’t reduce completion time by half, including physically interactive tasks, precise measurement, detailed visual review, and tasks requiring legal human execution.
    • E1: Used for tasks where direct access to an LLM like ChatGPT halves completion time, including text/code writing, editing, summarizing documents, answering questions, email responses, and creating training materials.
    • E2: Used for tasks where LLMs don’t reduce completion time by half, but additional software could be developed to do so, including summarizing long documents, retrieving facts, searching knowledge, making recommendations, and maintaining complex databases.
    • E3: Used for tasks where LLMs and image-related tools reduce completion time by half, including reading text from PDFs, scanning images, and creating/editing digital images.
  3. Report only the label that you determined for the task, which should match the label in step 1. Do not reply N/A.
  4. Given the amount of speculation required in step 1, describe your certainty about the estimate–either high, moderate, or low. Ensure that your response is consistent with the provided descriptions, seperating each of the four things with two new lines. Each of the things must start with its number followed by a colon and a space. For example, ‘1: This is the first thing.’ Do not return * as part of your response. Stick to the guidelines as the response will be parsed into four seperate columns.

Note that our approach builds on the work of Eisfeldt et al. (2023). Generative AI and Firm Values. There are four main differences between our work for this proposal and the original paper:
- Conceptual/Design:
+ We did not have task descriptions for each job title, so we also generated them using the OpenAI API. + We focused on the main task performed by the person with a specific job title. If we are funded*, we will assess multiple task descriptions for a given role, and we will ask ODOT to provide these task descriptions.
- Technical:
+ We used the latest OpenAI model, GPT-4 Turbo, to generate the exposure scores.
+ We also used the ChatCompletion API instead of the legacy completions API.

# Loading the required libraries:
# ------------------------------
import time

import pandas as pd
import datetime as dt
import numpy as np
import openai # pip install openai==0.28

from dotenv import load_dotenv


# Loading the environment variable:
# ---------------------------------
load_dotenv() # loads the OpenAI key from the .env file
## True
openai.api_key = os.getenv('OPENAI_API_KEY')


# Model to be used:
# -----------------
model_name = 'gpt-4-turbo-preview' # latest_model_name


# Task Data and Rubric:
# ---------------------
task = pd.read_csv('../data/salaries_tbl.csv') # read the entire dataset
task.rename(columns = {'job_title': 'occupation'}, inplace = True) # to reduce changes from paper below

# AI Firm Exposure Analysis based on
# https://www.nber.org/system/files/working_papers/w31222/w31222.pdf :
# ----------------------------------------------------------------------

rubric = {
    'E1': 'Label tasks E1 if direct access to the LLM through an interface like ChatGPT or the OpenAI playground alone can reduce the time it takes to complete the task with equivalent quality by at least half. This includes tasks that can be reduced to: - Writing and transforming text and code according to complex instructions, - Providing edits to existing text or code following specifications, - Writing code that can help perform a task that used to be done by hand, - Translating text between languages, - Summarizing medium-length documents, - Providing feedback on documents, - Answering questions about a document, - Generating questions a user might want to ask about a document, - Writing questions for an interview or assessment, - Writing and responding to emails, including ones that involve refuting information or engaging in a negotiation (but only if the negotiation is via written correspondence), - Maintain records of written data, - Prepare training materials based on general knowledge, or - Inform anyone of any information via any written or spoken medium.',
    'E2': 'Label tasks E2 if having access to the LLM alone may not reduce the time it takes to complete the task by at least half, but it is easy to imagine additional software that could be developed on top of the LLM that would reduce the time it takes to complete the task by half. This software may include capabilities such as: - Summarizing documents longer than 100,000 words and answering questions about those documents, - Retrieving up-to-date facts from the Internet and using those facts in combination with the LLM capabilities, - Searching over an organization’s existing knowledge, data, or documents and retrieving information, - Retrieving highly specialized domain knowledge, - Make recommendations given data or written input, - Analyze written information to inform decisions, - Prepare training materials based on highly specialized knowledge, - Provide counsel on issues, and - Maintain complex databases.',
    'E3': 'Suppose you had access to both the LLM and a system that could view, caption, and create images as well as any systems powered by the LLM (those in E2 above). This system cannot take video as an input and it cannot produce video as an output. This system cannot accurately retrieve very detailed information from image inputs, such as measurements of dimensions within an image. Label tasks as E3 if there is a significant reduction in the time it takes to complete the task given access to a LLM and these image capabilities: - Reading text from PDFs, - Scanning images, or - Creating or editing digital images according to instructions. The images can be realistic but they should not be detailed. The model can identify objects in the image but not relationships between those options',
    'E0': 'Label tasks E0 if none of the above clearly decrease the time it takes for an experienced worker to complete the task with high quality by at least half. Some examples: - If a task requires a high degree of human interaction (for example, in-person demonstrations) then it should be classified as E0. - If a task requires precise measurements then it should be classified as E0. - If a task requires reviewing visuals in detail then it should be classified as E0. - If a task requires any use of a hand or walking then it should be classified as E0. - Tools built on top of the LLM cannot make any decisions that might impact human livelihood (e.g.hiring, grading, etc.). If any part of the task involves collecting inputs to make a final decision (as opposed to analyzing data to inform a decision or make a recommendation) then it should be classified as E0. The LLM can make recommendations. - Even if tools built on top of the LLM can do a task, if using those tools would not save an experienced worker significant time completing the task, then it should be classified as E0. - The LLM and systems built on top of it cannot do anything that legally requires a human to perform the task. - If there is existing technology not powered by an LLM that is commonly used and can complete the task then you should mark the task E0 if using an LLM or LLM-powered tool will not further reduce the time to complete the task. When in doubt, you should default to E0.'
}

# Slightly Modified System Prompt:
# -----------------------------------
system_prompt = f"""
Consider the most powerful OpenAI large language model (LLM). This model can complete many tasks that can be formulated as having text input and text output where the context for the input can be captured in 100,000 words. The model also cannot draw up-to-date facts (those from <1 year ago) unless they are captured in the input. Assume you are a worker with an average level of expertise in your role trying to complete the given task. You have access to the LLM as well as any other existing software or computer hardware tools mentioned in the task. You also have access to any commonly available technical tools accessible via a laptop (e.g. a microphone, speakers, etc.). You do not have access to any other physical tools or materials. You are a helpful research assistant who wants to label the given tasks according to the rubric below. Equivalent quality means someone reviewing the work would not be able to tell whether a human completed it on their own or with assistance from the LLM. If you aren’t sure how to judge the amount of time a task takes, consider whether the tools described exposed the majority of subtasks associated with the task.

Rubric:
{rubric}

Your role:
You will be provided with an occupation (job title). Then, you must do four things. 1: Generate a 2-3 sentence description of a singular main task performed by the person with this title given that they work for the Ohio Department of Transportation. While they may be performing many tasks, provide the task that would be most representative of that job title. 2: Reason step by step to decide which of the labels (E0/E1/E2/E3) from the exposure rubric you were given applies to the task’s exposure to LLM. Report. Give an explanation for which label applies and report the label that you think fits best. Do not say zero or N/A. 3: Report only the label that you determined for the task, which should match the label in step 1. Do not reply N/A. 4: Given the amount of speculation required in step 1, describe your certainty about the estimate–either high, moderate, or low. Ensure that your response is consistent with the provided descriptions, seperating each of the four things with two new lines. Each of the things must start with its number followed by a colon and a space. For example, '1: This is the first thing.' Do not return * as part of your response. Stick to the guidelines as the response will be parsed into four seperate columns.
"""


# Function to Score the Exposure:
# -------------------------------
def score_exposure_and_explanation(occupation, rubric, system_prompt):
  
  prompt = f"\nOccupation: {occupation}\nRubric:{rubric}\nSystem Prompt{system_prompt}"
  
  # updated the response to use the ChatCompletion API instead of the legacy
  # completions API (that is why the previous code did not work)
  response = openai.ChatCompletion.create(
    model=model_name, 
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": prompt}
    ],
    temperature=0,
    max_tokens=500
    )
  
  # note also the explanation is now a bit different
  explanation = response.choices[0].message['content'].strip()
  
  return explanation

# Iterate through each occupation in our dataset and score the exposure:
# ----------------------------------------------------------------------
for index, row in task.iterrows():
    time.sleep(2) # to avoid rate limiting
    
    occupation = row['occupation']

   # Score the exposure level using the defined function
    try:
        gpt_response = score_exposure_and_explanation(occupation, rubric, system_prompt)
    except Exception as e:
        print(f"Error occurred while generating explanation for occupation '{occupation}': {str(e)}")
        gpt_response = "Error occurred while generating explanation"
    
    # Store the model, timestamp, explanation in the dataset
    task.at[index, 'gpt_model'] = model_name
    task.at[index, 'gpt_response_timestamp'] = dt.datetime.now()
    task.at[index, 'gpt_response'] = gpt_response
## Error occurred while generating explanation for occupation 'Sign Fabrication Superintenden': HTTP code 502 from API (<html>
## <head><title>502 Bad Gateway</title></head>
## <body>
## <center><h1>502 Bad Gateway</h1></center>
## <hr><center>cloudflare</center>
## </body>
## </html>
## )


# Save the dataset with the explanations:
# ----------------------------------------
# back to the original names
task.rename(columns = {'occupation' : 'job_title'}, inplace = True) 
task.to_excel('../data/task_statements_with_explanations.xlsx', index=False)

3 Results

3.1 Extracting the Results

The results of the exposure scores are stored in the file data/task_statements_with_explanations.xlsx. In the code chunk below, we divide the gpt_responses into four columns: task_description, ai_exposure, explanation and certainty.

# Cleaning the results:
# ---------------------
results = readxl::read_excel("../data/task_statements_with_explanations.xlsx") |> 
  # capitalize on the fact that each piece is separated by two new lines and 
  # starts with number followed by a colon and a space
  dplyr::mutate(
    task_description = stringr::str_extract(gpt_response, "1:.*\n\n2:") |> stringr::str_remove("1: ") |> stringr::str_remove("\n\n2:"),
    ai_exposure = stringr::str_extract(gpt_response, "3:.*\n\n4:") |> stringr::str_remove("3: ") |> stringr::str_remove("\n\n4:"),
    explanation = stringr::str_extract(gpt_response, "2:.*\n\n3:") |> stringr::str_remove("2: ") |> stringr::str_remove("\n\n3:"),
    certainty = stringr::str_extract(gpt_response, "4:.*$") |> stringr::str_remove("4: ") |> 
      # remove text that starts with '. The reasoning' and ends with '.'
      stringr::str_remove("\\. The reasoning.*\\.") |> 
      # similarly, remove text that starts with '. The assessment' and ends with '.'
      stringr::str_remove("\\. The assessment.*\\.")
  )

# Save the results:
# -----------------
readr::write_csv(results, "../data/task_statements_with_formatted_explanations.csv")

# Display the results:
# --------------------
DT::datatable(
  results |> dplyr::select(job_title, count, task_description, ai_exposure, explanation, certainty), 
  rownames = FALSE, 
  extensions = 'Buttons',
  options = list(
    pageLength = 15, scrollX = TRUE, autoWidth = TRUE,
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf', 'print') 
    )
)

3.2 Summary of the Results

3.2.1 Statistical Summaries

To summarize the results, we will first provide some summary statistics that capture the distribution of the exposure scores across the job titles. Then, we will provide a brief description of the exposure scores and the certainty of the estimates.

# Summary Statistics:
# --------------------
summary_results = results |> 
  dplyr::group_by(ai_exposure) |> 
  dplyr::summarize(
    num_job_titles = dplyr::n(),
    num_jobs = sum(count),
    mode_certainty = VIM::maxCat(certainty)
  )

# Display the summary statistics:
# -------------------------------
DT::datatable(
  summary_results, 
  rownames = FALSE, 
  extensions = 'Buttons',
  options = list(
    pageLength = 15, scrollX = TRUE, autoWidth = TRUE,
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf', 'print') 
    )
)

3.2.2 Tree Map of the Exposure Scores

To visualize the distribution of the exposure scores across the job titles, we will use a tree map. The tree map will provide a visual representation of the exposure scores and the number of job titles in each category.

treemap::treemap(
  results,
  index = c("ai_exposure", "job_title"),
  vSize = "count",
  vColor = "ai_exposure",
  palette = c("#B61E2E", "#828282", "#FFFFFF"),
  fontsize.labels=c(24,12),
  fontface.labels=c(2,2),
  border.col=c("black","#2F4F4F"), 
  border.lwds=c(7,2),
  align.labels=list(
    c("left", "top"), 
    c("center", "center")
  ),  
  overlap.labels=1, 
  inflate.labels=F,  
  title="AI Exposure Scores by Job Title: Area Proportional to the Number of Jobs",
  position.legend = 'none',
  force.print.labels = FALSE,
  draw=TRUE
)

LS0tDQp0aXRsZTogIlByZWxpbWluYXJ5IEFuYWx5c2lzIGZvciBBSS9NTCBPRE9UIg0KYXV0aG9yOiAiRmFkZWwgTS4gTWVnYWhlZCINCmRhdGU6ICIyMDI0LTAzLTAyIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGNhY2hlID0gVFJVRSkNCmBgYA0KDQojIEdldHRpbmcgdGhlIERhdGENCg0KVG8gZ2V0IGluZm9ybWF0aW9uIGFib3V0IHRoZSBqb2IgdGl0bGVzIGFuZCBzYWxhcmllcywgSSBkb3dubG9hZGVkIHRoZSBkYXRhIGZyb20gdGhlIFtPaGlvIENoZWNrYm9va10oaHR0cHM6Ly9jaGVja2Jvb2sub2hpby5nb3YvU2FsYXJpZXMvU3RhdGUuYXNweD9saW5rRnJvbT1TdGF0ZSUyMFBvcHVsYXIlMjBTZWFyY2hlcykgb24gTWFyY2ggMSwgMjAyNCBhdCAzOjUwIFBNLiBUaGUgZGF0YSBpcyBzdG9yZWQgaW4gdGhlIGZpbGUgYGRhdGEvRW1wbG95ZWVQYXlyb2xsLmNzdmAuIEluIHRoZSBjb2RlIGNodW5rIGJlbG93LCBJOiAgIA0KDQogIC0gcmVhZCB0aGUgZGF0YSBhbmQgY2xlYW5lZCBpdCB1cDsgIA0KICAtIGNvdW50ZWQgdGhlIG51bWJlciBvZiBlbXBsb3llZXMgaW4gZWFjaCBqb2IgdGl0bGU7IGFuZCB0aGVuICANCiAgLSBzYXZlZCB0aGUgcmVzdWx0IGluIGBkYXRhL3NhbGFyaWVzX3RibC5jc3ZgLiANCg0KTm90ZSB0aGF0IHRoZXJlIGlzIGEgdG90YWwgb2YgKioyMjEgdW5pcXVlIGpvYiB0aXRsZXMqKiBhdCB0aGUgT2hpbyBEZXBhcnRtZW50IG9mIFRyYW5zcG9ydGF0aW9uLCBiYXNlZCBvbiB0aGUgZGF0YSBkb3dubG9hZGVkIGZyb20gdGhlIFtPaGlvIENoZWNrYm9va10oaHR0cHM6Ly9jaGVja2Jvb2sub2hpby5nb3YvU2FsYXJpZXMvU3RhdGUuYXNweD9saW5rRnJvbT1TdGF0ZSUyMFBvcHVsYXIlMjBTZWFyY2hlcykgb24gTWFyY2ggMSwgMjAyNCBhdCAzOjUwIFBNLiANCg0KYGBge3IgZ2V0X2RhdGF9DQpzYWxhcmllcyA9IHJlYWRyOjpyZWFkX2NzdigiLi4vZGF0YS9FbXBsb3llZVBheXJvbGwuY3N2IikgfD4gDQogIGphbml0b3I6OmNsZWFuX25hbWVzKCkNCiAgDQpzYWxhcmllcyA9IHNhbGFyaWVzIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgbWF4X2hvdXJseV9yYXRlID0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKG1heF9ob3VybHlfcmF0ZSwgIlxcJHwsIiwgIiIpIHw+IGFzLm51bWVyaWMoKSwNCiAgICBhbW91bnQgPSBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoYW1vdW50LCAiXFwkfCwiLCAiIikgfD4gYXMubnVtZXJpYygpDQogICkNCg0KcG9zaXRpb25fdGFibGVzID0gdGFibGUoc2FsYXJpZXMkam9iX3RpdGxlKQ0KDQpzYWxhcmllc190YmwgPSB0aWJibGU6OnRpYmJsZSgNCiAgaW5kZXggPSAxOmxlbmd0aChwb3NpdGlvbl90YWJsZXMpLA0KICBqb2JfdGl0bGUgPSBuYW1lcyhwb3NpdGlvbl90YWJsZXMpLA0KICBjb3VudCA9IHBvc2l0aW9uX3RhYmxlcyB8PiBhcy5pbnRlZ2VyKCkNCikNCg0KcmVhZHI6OndyaXRlX2NzdihzYWxhcmllc190YmwsICIuLi9kYXRhL3NhbGFyaWVzX3RibC5jc3YiKQ0KDQpEVDo6ZGF0YXRhYmxlKA0KICBzYWxhcmllc190YmwgfD4gZHBseXI6OnNlbGVjdChqb2JfdGl0bGUsIGNvdW50KSwgDQogIHJvd25hbWVzID0gRkFMU0UsIA0KICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLA0KICBvcHRpb25zID0gbGlzdCgNCiAgICBwYWdlTGVuZ3RoID0gMTUsIHNjcm9sbFggPSBUUlVFLCBhdXRvV2lkdGggPSBUUlVFLA0KICAgIGRvbSA9ICdCZnJ0aXAnLA0KICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSANCiAgICApDQopDQpgYGANCg0KDQojIFByb2plY3RlZCBUYXNrcyBmb3IgRWFjaCBKb2IgVGl0bGUgYW5kIGhvdyB0aGV5IHdvdWxkIGJlIEltcGFjdGVkIGJ5IEFJDQoNCiMjIFVzaW5nIE9wZW5BSSBBUEkgdG8gR2VuZXJhdGUgVGFzayBEZXNjcmlwdGlvbnMgYW5kIEV4cG9zdXJlIFNjb3Jlcw0KDQpEdWUgdG8gdGhlIGxhY2sgb2YgT2hpbyBEZXBhcnRtZW50IG9mIFRyYW5zcG9ydGF0aW9uIChPRE9UKSBkYXRhIGluIHRoZSBwcm9wb3NhbCBzdGFnZSwgd2Ugd2lsbCB1dGlsaXplIHRoZSBPcGVuQUkgQVBJIHRvIHBlcmZvcm0gdGhlIGZvbGxvd2luZyBmb3VyIHRhc2tzIGZvciBlYWNoIGpvYiB0aXRsZSBpbiB0aGUgZGF0YXNldDogICANCg0KICAxLiBHZW5lcmF0ZSBhIDItMyBzZW50ZW5jZSBkZXNjcmlwdGlvbiBvZiBhIHNpbmd1bGFyIG1haW4gdGFzayBwZXJmb3JtZWQgYnkgdGhlIHBlcnNvbiB3aXRoIHRoaXMgdGl0bGUgZ2l2ZW4gdGhhdCB0aGV5IHdvcmsgZm9yIHRoZSBPaGlvIERlcGFydG1lbnQgb2YgVHJhbnNwb3J0YXRpb24uIFdoaWxlIHRoZXkgbWF5IGJlIHBlcmZvcm1pbmcgbWFueSB0YXNrcywgcHJvdmlkZSB0aGUgdGFzayB0aGF0IHdvdWxkIGJlIG1vc3QgcmVwcmVzZW50YXRpdmUgb2YgdGhhdCBqb2IgdGl0bGUuICANCiAgMi4gUmVhc29uIHN0ZXAgYnkgc3RlcCB0byBkZWNpZGUgd2hpY2ggb2YgdGhlIGxhYmVscyAoRTAvRTEvRTIvRTMpIGZyb20gdGhlIGV4cG9zdXJlIHJ1YnJpYyB5b3Ugd2VyZSBnaXZlbiBhcHBsaWVzIHRvIHRoZSB0YXNr4oCZcyBleHBvc3VyZSB0byBMTE0uIFJlcG9ydC4gR2l2ZSBhbiBleHBsYW5hdGlvbiBmb3Igd2hpY2ggbGFiZWwgYXBwbGllcyBhbmQgcmVwb3J0IHRoZSBsYWJlbCB0aGF0IHlvdSB0aGluayBmaXRzIGJlc3QuIERvIG5vdCBzYXkgemVybyBvciBOL0EuIE5vdGUgdGhhdCB0aGUgbGFiZWxzIGNvcnJlc3BvbmQgdG86ICAgIA0KICAgICAgKyAqKkUwOioqIFVzZWQgZm9yIHRhc2tzIHdoZXJlIExMTXMgYW5kIHJlbGF0ZWQgdG9vbHMgZG9uJ3QgcmVkdWNlIGNvbXBsZXRpb24gdGltZSBieSBoYWxmLCBpbmNsdWRpbmcgcGh5c2ljYWxseSBpbnRlcmFjdGl2ZSB0YXNrcywgcHJlY2lzZSBtZWFzdXJlbWVudCwgZGV0YWlsZWQgdmlzdWFsIHJldmlldywgYW5kIHRhc2tzIHJlcXVpcmluZyBsZWdhbCBodW1hbiBleGVjdXRpb24uICAgIA0KICAgICAgKyAqKkUxOioqIFVzZWQgZm9yIHRhc2tzIHdoZXJlIGRpcmVjdCBhY2Nlc3MgdG8gYW4gTExNIGxpa2UgQ2hhdEdQVCBoYWx2ZXMgY29tcGxldGlvbiB0aW1lLCBpbmNsdWRpbmcgdGV4dC9jb2RlIHdyaXRpbmcsIGVkaXRpbmcsIHN1bW1hcml6aW5nIGRvY3VtZW50cywgYW5zd2VyaW5nIHF1ZXN0aW9ucywgZW1haWwgcmVzcG9uc2VzLCBhbmQgY3JlYXRpbmcgdHJhaW5pbmcgbWF0ZXJpYWxzLiAgDQogICAgICArICoqRTI6KiogVXNlZCBmb3IgdGFza3Mgd2hlcmUgTExNcyBkb24ndCByZWR1Y2UgY29tcGxldGlvbiB0aW1lIGJ5IGhhbGYsIGJ1dCBhZGRpdGlvbmFsIHNvZnR3YXJlIGNvdWxkIGJlIGRldmVsb3BlZCB0byBkbyBzbywgaW5jbHVkaW5nIHN1bW1hcml6aW5nIGxvbmcgZG9jdW1lbnRzLCByZXRyaWV2aW5nIGZhY3RzLCBzZWFyY2hpbmcga25vd2xlZGdlLCBtYWtpbmcgcmVjb21tZW5kYXRpb25zLCBhbmQgbWFpbnRhaW5pbmcgY29tcGxleCBkYXRhYmFzZXMuICANCiAgICAgICsgKipFMzoqKiBVc2VkIGZvciB0YXNrcyB3aGVyZSBMTE1zIGFuZCBpbWFnZS1yZWxhdGVkIHRvb2xzIHJlZHVjZSBjb21wbGV0aW9uIHRpbWUgYnkgaGFsZiwgaW5jbHVkaW5nIHJlYWRpbmcgdGV4dCBmcm9tIFBERnMsIHNjYW5uaW5nIGltYWdlcywgYW5kIGNyZWF0aW5nL2VkaXRpbmcgZGlnaXRhbCBpbWFnZXMuICANCiAgMy4gUmVwb3J0IG9ubHkgdGhlIGxhYmVsIHRoYXQgeW91IGRldGVybWluZWQgZm9yIHRoZSB0YXNrLCB3aGljaCBzaG91bGQgbWF0Y2ggdGhlIGxhYmVsIGluIHN0ZXAgMS4gRG8gbm90IHJlcGx5IE4vQS4gIA0KICA0LiBHaXZlbiB0aGUgYW1vdW50IG9mIHNwZWN1bGF0aW9uIHJlcXVpcmVkIGluIHN0ZXAgMSwgZGVzY3JpYmUgeW91ciBjZXJ0YWludHkgYWJvdXQgdGhlIGVzdGltYXRl4oCTZWl0aGVyIGhpZ2gsIG1vZGVyYXRlLCBvciBsb3cuIEVuc3VyZSB0aGF0IHlvdXIgcmVzcG9uc2UgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBwcm92aWRlZCBkZXNjcmlwdGlvbnMsIHNlcGVyYXRpbmcgZWFjaCBvZiB0aGUgZm91ciB0aGluZ3Mgd2l0aCB0d28gbmV3IGxpbmVzLiBFYWNoIG9mIHRoZSB0aGluZ3MgbXVzdCBzdGFydCB3aXRoIGl0cyBudW1iZXIgZm9sbG93ZWQgYnkgYSBjb2xvbiBhbmQgYSBzcGFjZS4gRm9yIGV4YW1wbGUsICcxOiBUaGlzIGlzIHRoZSBmaXJzdCB0aGluZy4nIERvIG5vdCByZXR1cm4gKiBhcyBwYXJ0IG9mIHlvdXIgcmVzcG9uc2UuIFN0aWNrIHRvIHRoZSBndWlkZWxpbmVzIGFzIHRoZSByZXNwb25zZSB3aWxsIGJlIHBhcnNlZCBpbnRvIGZvdXIgc2VwZXJhdGUgY29sdW1ucy4gIA0KICANCk5vdGUgdGhhdCBvdXIgYXBwcm9hY2ggYnVpbGRzIG9uIHRoZSB3b3JrIG9mIFtFaXNmZWxkdCBldCBhbC4gKDIwMjMpLiBHZW5lcmF0aXZlIEFJIGFuZCBGaXJtIFZhbHVlc10oaHR0cHM6Ly93d3cubmJlci5vcmcvc3lzdGVtL2ZpbGVzL3dvcmtpbmdfcGFwZXJzL3czMTIyMi93MzEyMjIucGRmKS4gVGhlcmUgYXJlIGZvdXIgbWFpbiBkaWZmZXJlbmNlcyBiZXR3ZWVuIG91ciB3b3JrIGZvciB0aGlzIHByb3Bvc2FsIGFuZCB0aGUgb3JpZ2luYWwgcGFwZXI6ICANCiAgLSAqKkNvbmNlcHR1YWwvRGVzaWduOioqICANCiAgICArIFdlIGRpZCBub3QgaGF2ZSB0YXNrIGRlc2NyaXB0aW9ucyBmb3IgZWFjaCBqb2IgdGl0bGUsIHNvIHdlIGFsc28gZ2VuZXJhdGVkIHRoZW0gdXNpbmcgdGhlIE9wZW5BSSBBUEkuIA0KICAgICsgV2UgZm9jdXNlZCBvbiB0aGUgbWFpbiB0YXNrIHBlcmZvcm1lZCBieSB0aGUgcGVyc29uIHdpdGggYSBzcGVjaWZpYyBqb2IgdGl0bGUuIElmIHdlIGFyZSAqKmZ1bmRlZCoqKiwgd2Ugd2lsbCBhc3Nlc3MgbXVsdGlwbGUgdGFzayBkZXNjcmlwdGlvbnMgZm9yIGEgZ2l2ZW4gcm9sZSwgYW5kIHdlIHdpbGwgYXNrIE9ET1QgdG8gcHJvdmlkZSB0aGVzZSB0YXNrIGRlc2NyaXB0aW9ucy4gIA0KICAtICoqVGVjaG5pY2FsOioqICANCiAgICArIFdlIHVzZWQgdGhlIGxhdGVzdCBPcGVuQUkgbW9kZWwsIEdQVC00IFR1cmJvLCB0byBnZW5lcmF0ZSB0aGUgZXhwb3N1cmUgc2NvcmVzLiAgDQogICAgKyBXZSBhbHNvIHVzZWQgdGhlIENoYXRDb21wbGV0aW9uIEFQSSBpbnN0ZWFkIG9mIHRoZSBsZWdhY3kgY29tcGxldGlvbnMgQVBJLiAgDQogICAgDQoNCmBgYHtweXRob24gZ2V0X2V4cG9zdXJlLCBjYWNoZT1UUlVFfQ0KIyBMb2FkaW5nIHRoZSByZXF1aXJlZCBsaWJyYXJpZXM6DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KaW1wb3J0IHRpbWUNCg0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IGRhdGV0aW1lIGFzIGR0DQppbXBvcnQgbnVtcHkgYXMgbnANCmltcG9ydCBvcGVuYWkgIyBwaXAgaW5zdGFsbCBvcGVuYWk9PTAuMjgNCg0KZnJvbSBkb3RlbnYgaW1wb3J0IGxvYWRfZG90ZW52DQoNCg0KIyBMb2FkaW5nIHRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZToNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpsb2FkX2RvdGVudigpICMgbG9hZHMgdGhlIE9wZW5BSSBrZXkgZnJvbSB0aGUgLmVudiBmaWxlDQpvcGVuYWkuYXBpX2tleSA9IG9zLmdldGVudignT1BFTkFJX0FQSV9LRVknKQ0KDQoNCiMgTW9kZWwgdG8gYmUgdXNlZDoNCiMgLS0tLS0tLS0tLS0tLS0tLS0NCm1vZGVsX25hbWUgPSAnZ3B0LTQtdHVyYm8tcHJldmlldycgIyBsYXRlc3RfbW9kZWxfbmFtZQ0KDQoNCiMgVGFzayBEYXRhIGFuZCBSdWJyaWM6DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KdGFzayA9IHBkLnJlYWRfY3N2KCcuLi9kYXRhL3NhbGFyaWVzX3RibC5jc3YnKSAjIHJlYWQgdGhlIGVudGlyZSBkYXRhc2V0DQp0YXNrLnJlbmFtZShjb2x1bW5zID0geydqb2JfdGl0bGUnOiAnb2NjdXBhdGlvbid9LCBpbnBsYWNlID0gVHJ1ZSkgIyB0byByZWR1Y2UgY2hhbmdlcyBmcm9tIHBhcGVyIGJlbG93DQoNCiMgQUkgRmlybSBFeHBvc3VyZSBBbmFseXNpcyBiYXNlZCBvbg0KIyBodHRwczovL3d3dy5uYmVyLm9yZy9zeXN0ZW0vZmlsZXMvd29ya2luZ19wYXBlcnMvdzMxMjIyL3czMTIyMi5wZGYgOg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJ1YnJpYyA9IHsNCiAgICAnRTEnOiAnTGFiZWwgdGFza3MgRTEgaWYgZGlyZWN0IGFjY2VzcyB0byB0aGUgTExNIHRocm91Z2ggYW4gaW50ZXJmYWNlIGxpa2UgQ2hhdEdQVCBvciB0aGUgT3BlbkFJIHBsYXlncm91bmQgYWxvbmUgY2FuIHJlZHVjZSB0aGUgdGltZSBpdCB0YWtlcyB0byBjb21wbGV0ZSB0aGUgdGFzayB3aXRoIGVxdWl2YWxlbnQgcXVhbGl0eSBieSBhdCBsZWFzdCBoYWxmLiBUaGlzIGluY2x1ZGVzIHRhc2tzIHRoYXQgY2FuIGJlIHJlZHVjZWQgdG86IC0gV3JpdGluZyBhbmQgdHJhbnNmb3JtaW5nIHRleHQgYW5kIGNvZGUgYWNjb3JkaW5nIHRvIGNvbXBsZXggaW5zdHJ1Y3Rpb25zLCAtIFByb3ZpZGluZyBlZGl0cyB0byBleGlzdGluZyB0ZXh0IG9yIGNvZGUgZm9sbG93aW5nIHNwZWNpZmljYXRpb25zLCAtIFdyaXRpbmcgY29kZSB0aGF0IGNhbiBoZWxwIHBlcmZvcm0gYSB0YXNrIHRoYXQgdXNlZCB0byBiZSBkb25lIGJ5IGhhbmQsIC0gVHJhbnNsYXRpbmcgdGV4dCBiZXR3ZWVuIGxhbmd1YWdlcywgLSBTdW1tYXJpemluZyBtZWRpdW0tbGVuZ3RoIGRvY3VtZW50cywgLSBQcm92aWRpbmcgZmVlZGJhY2sgb24gZG9jdW1lbnRzLCAtIEFuc3dlcmluZyBxdWVzdGlvbnMgYWJvdXQgYSBkb2N1bWVudCwgLSBHZW5lcmF0aW5nIHF1ZXN0aW9ucyBhIHVzZXIgbWlnaHQgd2FudCB0byBhc2sgYWJvdXQgYSBkb2N1bWVudCwgLSBXcml0aW5nIHF1ZXN0aW9ucyBmb3IgYW4gaW50ZXJ2aWV3IG9yIGFzc2Vzc21lbnQsIC0gV3JpdGluZyBhbmQgcmVzcG9uZGluZyB0byBlbWFpbHMsIGluY2x1ZGluZyBvbmVzIHRoYXQgaW52b2x2ZSByZWZ1dGluZyBpbmZvcm1hdGlvbiBvciBlbmdhZ2luZyBpbiBhIG5lZ290aWF0aW9uIChidXQgb25seSBpZiB0aGUgbmVnb3RpYXRpb24gaXMgdmlhIHdyaXR0ZW4gY29ycmVzcG9uZGVuY2UpLCAtIE1haW50YWluIHJlY29yZHMgb2Ygd3JpdHRlbiBkYXRhLCAtIFByZXBhcmUgdHJhaW5pbmcgbWF0ZXJpYWxzIGJhc2VkIG9uIGdlbmVyYWwga25vd2xlZGdlLCBvciAtIEluZm9ybSBhbnlvbmUgb2YgYW55IGluZm9ybWF0aW9uIHZpYSBhbnkgd3JpdHRlbiBvciBzcG9rZW4gbWVkaXVtLicsDQogICAgJ0UyJzogJ0xhYmVsIHRhc2tzIEUyIGlmIGhhdmluZyBhY2Nlc3MgdG8gdGhlIExMTSBhbG9uZSBtYXkgbm90IHJlZHVjZSB0aGUgdGltZSBpdCB0YWtlcyB0byBjb21wbGV0ZSB0aGUgdGFzayBieSBhdCBsZWFzdCBoYWxmLCBidXQgaXQgaXMgZWFzeSB0byBpbWFnaW5lIGFkZGl0aW9uYWwgc29mdHdhcmUgdGhhdCBjb3VsZCBiZSBkZXZlbG9wZWQgb24gdG9wIG9mIHRoZSBMTE0gdGhhdCB3b3VsZCByZWR1Y2UgdGhlIHRpbWUgaXQgdGFrZXMgdG8gY29tcGxldGUgdGhlIHRhc2sgYnkgaGFsZi4gVGhpcyBzb2Z0d2FyZSBtYXkgaW5jbHVkZSBjYXBhYmlsaXRpZXMgc3VjaCBhczogLSBTdW1tYXJpemluZyBkb2N1bWVudHMgbG9uZ2VyIHRoYW4gMTAwLDAwMCB3b3JkcyBhbmQgYW5zd2VyaW5nIHF1ZXN0aW9ucyBhYm91dCB0aG9zZSBkb2N1bWVudHMsIC0gUmV0cmlldmluZyB1cC10by1kYXRlIGZhY3RzIGZyb20gdGhlIEludGVybmV0IGFuZCB1c2luZyB0aG9zZSBmYWN0cyBpbiBjb21iaW5hdGlvbiB3aXRoIHRoZSBMTE0gY2FwYWJpbGl0aWVzLCAtIFNlYXJjaGluZyBvdmVyIGFuIG9yZ2FuaXphdGlvbuKAmXMgZXhpc3Rpbmcga25vd2xlZGdlLCBkYXRhLCBvciBkb2N1bWVudHMgYW5kIHJldHJpZXZpbmcgaW5mb3JtYXRpb24sIC0gUmV0cmlldmluZyBoaWdobHkgc3BlY2lhbGl6ZWQgZG9tYWluIGtub3dsZWRnZSwgLSBNYWtlIHJlY29tbWVuZGF0aW9ucyBnaXZlbiBkYXRhIG9yIHdyaXR0ZW4gaW5wdXQsIC0gQW5hbHl6ZSB3cml0dGVuIGluZm9ybWF0aW9uIHRvIGluZm9ybSBkZWNpc2lvbnMsIC0gUHJlcGFyZSB0cmFpbmluZyBtYXRlcmlhbHMgYmFzZWQgb24gaGlnaGx5IHNwZWNpYWxpemVkIGtub3dsZWRnZSwgLSBQcm92aWRlIGNvdW5zZWwgb24gaXNzdWVzLCBhbmQgLSBNYWludGFpbiBjb21wbGV4IGRhdGFiYXNlcy4nLA0KICAgICdFMyc6ICdTdXBwb3NlIHlvdSBoYWQgYWNjZXNzIHRvIGJvdGggdGhlIExMTSBhbmQgYSBzeXN0ZW0gdGhhdCBjb3VsZCB2aWV3LCBjYXB0aW9uLCBhbmQgY3JlYXRlIGltYWdlcyBhcyB3ZWxsIGFzIGFueSBzeXN0ZW1zIHBvd2VyZWQgYnkgdGhlIExMTSAodGhvc2UgaW4gRTIgYWJvdmUpLiBUaGlzIHN5c3RlbSBjYW5ub3QgdGFrZSB2aWRlbyBhcyBhbiBpbnB1dCBhbmQgaXQgY2Fubm90IHByb2R1Y2UgdmlkZW8gYXMgYW4gb3V0cHV0LiBUaGlzIHN5c3RlbSBjYW5ub3QgYWNjdXJhdGVseSByZXRyaWV2ZSB2ZXJ5IGRldGFpbGVkIGluZm9ybWF0aW9uIGZyb20gaW1hZ2UgaW5wdXRzLCBzdWNoIGFzIG1lYXN1cmVtZW50cyBvZiBkaW1lbnNpb25zIHdpdGhpbiBhbiBpbWFnZS4gTGFiZWwgdGFza3MgYXMgRTMgaWYgdGhlcmUgaXMgYSBzaWduaWZpY2FudCByZWR1Y3Rpb24gaW4gdGhlIHRpbWUgaXQgdGFrZXMgdG8gY29tcGxldGUgdGhlIHRhc2sgZ2l2ZW4gYWNjZXNzIHRvIGEgTExNIGFuZCB0aGVzZSBpbWFnZSBjYXBhYmlsaXRpZXM6IC0gUmVhZGluZyB0ZXh0IGZyb20gUERGcywgLSBTY2FubmluZyBpbWFnZXMsIG9yIC0gQ3JlYXRpbmcgb3IgZWRpdGluZyBkaWdpdGFsIGltYWdlcyBhY2NvcmRpbmcgdG8gaW5zdHJ1Y3Rpb25zLiBUaGUgaW1hZ2VzIGNhbiBiZSByZWFsaXN0aWMgYnV0IHRoZXkgc2hvdWxkIG5vdCBiZSBkZXRhaWxlZC4gVGhlIG1vZGVsIGNhbiBpZGVudGlmeSBvYmplY3RzIGluIHRoZSBpbWFnZSBidXQgbm90IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aG9zZSBvcHRpb25zJywNCiAgICAnRTAnOiAnTGFiZWwgdGFza3MgRTAgaWYgbm9uZSBvZiB0aGUgYWJvdmUgY2xlYXJseSBkZWNyZWFzZSB0aGUgdGltZSBpdCB0YWtlcyBmb3IgYW4gZXhwZXJpZW5jZWQgd29ya2VyIHRvIGNvbXBsZXRlIHRoZSB0YXNrIHdpdGggaGlnaCBxdWFsaXR5IGJ5IGF0IGxlYXN0IGhhbGYuIFNvbWUgZXhhbXBsZXM6IC0gSWYgYSB0YXNrIHJlcXVpcmVzIGEgaGlnaCBkZWdyZWUgb2YgaHVtYW4gaW50ZXJhY3Rpb24gKGZvciBleGFtcGxlLCBpbi1wZXJzb24gZGVtb25zdHJhdGlvbnMpIHRoZW4gaXQgc2hvdWxkIGJlIGNsYXNzaWZpZWQgYXMgRTAuIC0gSWYgYSB0YXNrIHJlcXVpcmVzIHByZWNpc2UgbWVhc3VyZW1lbnRzIHRoZW4gaXQgc2hvdWxkIGJlIGNsYXNzaWZpZWQgYXMgRTAuIC0gSWYgYSB0YXNrIHJlcXVpcmVzIHJldmlld2luZyB2aXN1YWxzIGluIGRldGFpbCB0aGVuIGl0IHNob3VsZCBiZSBjbGFzc2lmaWVkIGFzIEUwLiAtIElmIGEgdGFzayByZXF1aXJlcyBhbnkgdXNlIG9mIGEgaGFuZCBvciB3YWxraW5nIHRoZW4gaXQgc2hvdWxkIGJlIGNsYXNzaWZpZWQgYXMgRTAuIC0gVG9vbHMgYnVpbHQgb24gdG9wIG9mIHRoZSBMTE0gY2Fubm90IG1ha2UgYW55IGRlY2lzaW9ucyB0aGF0IG1pZ2h0IGltcGFjdCBodW1hbiBsaXZlbGlob29kIChlLmcuaGlyaW5nLCBncmFkaW5nLCBldGMuKS4gSWYgYW55IHBhcnQgb2YgdGhlIHRhc2sgaW52b2x2ZXMgY29sbGVjdGluZyBpbnB1dHMgdG8gbWFrZSBhIGZpbmFsIGRlY2lzaW9uIChhcyBvcHBvc2VkIHRvIGFuYWx5emluZyBkYXRhIHRvIGluZm9ybSBhIGRlY2lzaW9uIG9yIG1ha2UgYSByZWNvbW1lbmRhdGlvbikgdGhlbiBpdCBzaG91bGQgYmUgY2xhc3NpZmllZCBhcyBFMC4gVGhlIExMTSBjYW4gbWFrZSByZWNvbW1lbmRhdGlvbnMuIC0gRXZlbiBpZiB0b29scyBidWlsdCBvbiB0b3Agb2YgdGhlIExMTSBjYW4gZG8gYSB0YXNrLCBpZiB1c2luZyB0aG9zZSB0b29scyB3b3VsZCBub3Qgc2F2ZSBhbiBleHBlcmllbmNlZCB3b3JrZXIgc2lnbmlmaWNhbnQgdGltZSBjb21wbGV0aW5nIHRoZSB0YXNrLCB0aGVuIGl0IHNob3VsZCBiZSBjbGFzc2lmaWVkIGFzIEUwLiAtIFRoZSBMTE0gYW5kIHN5c3RlbXMgYnVpbHQgb24gdG9wIG9mIGl0IGNhbm5vdCBkbyBhbnl0aGluZyB0aGF0IGxlZ2FsbHkgcmVxdWlyZXMgYSBodW1hbiB0byBwZXJmb3JtIHRoZSB0YXNrLiAtIElmIHRoZXJlIGlzIGV4aXN0aW5nIHRlY2hub2xvZ3kgbm90IHBvd2VyZWQgYnkgYW4gTExNIHRoYXQgaXMgY29tbW9ubHkgdXNlZCBhbmQgY2FuIGNvbXBsZXRlIHRoZSB0YXNrIHRoZW4geW91IHNob3VsZCBtYXJrIHRoZSB0YXNrIEUwIGlmIHVzaW5nIGFuIExMTSBvciBMTE0tcG93ZXJlZCB0b29sIHdpbGwgbm90IGZ1cnRoZXIgcmVkdWNlIHRoZSB0aW1lIHRvIGNvbXBsZXRlIHRoZSB0YXNrLiBXaGVuIGluIGRvdWJ0LCB5b3Ugc2hvdWxkIGRlZmF1bHQgdG8gRTAuJw0KfQ0KDQojIFNsaWdodGx5IE1vZGlmaWVkIFN5c3RlbSBQcm9tcHQ6DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpzeXN0ZW1fcHJvbXB0ID0gZiIiIg0KQ29uc2lkZXIgdGhlIG1vc3QgcG93ZXJmdWwgT3BlbkFJIGxhcmdlIGxhbmd1YWdlIG1vZGVsIChMTE0pLiBUaGlzIG1vZGVsIGNhbiBjb21wbGV0ZSBtYW55IHRhc2tzIHRoYXQgY2FuIGJlIGZvcm11bGF0ZWQgYXMgaGF2aW5nIHRleHQgaW5wdXQgYW5kIHRleHQgb3V0cHV0IHdoZXJlIHRoZSBjb250ZXh0IGZvciB0aGUgaW5wdXQgY2FuIGJlIGNhcHR1cmVkIGluIDEwMCwwMDAgd29yZHMuIFRoZSBtb2RlbCBhbHNvIGNhbm5vdCBkcmF3IHVwLXRvLWRhdGUgZmFjdHMgKHRob3NlIGZyb20gPDEgeWVhciBhZ28pIHVubGVzcyB0aGV5IGFyZSBjYXB0dXJlZCBpbiB0aGUgaW5wdXQuIEFzc3VtZSB5b3UgYXJlIGEgd29ya2VyIHdpdGggYW4gYXZlcmFnZSBsZXZlbCBvZiBleHBlcnRpc2UgaW4geW91ciByb2xlIHRyeWluZyB0byBjb21wbGV0ZSB0aGUgZ2l2ZW4gdGFzay4gWW91IGhhdmUgYWNjZXNzIHRvIHRoZSBMTE0gYXMgd2VsbCBhcyBhbnkgb3RoZXIgZXhpc3Rpbmcgc29mdHdhcmUgb3IgY29tcHV0ZXIgaGFyZHdhcmUgdG9vbHMgbWVudGlvbmVkIGluIHRoZSB0YXNrLiBZb3UgYWxzbyBoYXZlIGFjY2VzcyB0byBhbnkgY29tbW9ubHkgYXZhaWxhYmxlIHRlY2huaWNhbCB0b29scyBhY2Nlc3NpYmxlIHZpYSBhIGxhcHRvcCAoZS5nLiBhIG1pY3JvcGhvbmUsIHNwZWFrZXJzLCBldGMuKS4gWW91IGRvIG5vdCBoYXZlIGFjY2VzcyB0byBhbnkgb3RoZXIgcGh5c2ljYWwgdG9vbHMgb3IgbWF0ZXJpYWxzLiBZb3UgYXJlIGEgaGVscGZ1bCByZXNlYXJjaCBhc3Npc3RhbnQgd2hvIHdhbnRzIHRvIGxhYmVsIHRoZSBnaXZlbiB0YXNrcyBhY2NvcmRpbmcgdG8gdGhlIHJ1YnJpYyBiZWxvdy4gRXF1aXZhbGVudCBxdWFsaXR5IG1lYW5zIHNvbWVvbmUgcmV2aWV3aW5nIHRoZSB3b3JrIHdvdWxkIG5vdCBiZSBhYmxlIHRvIHRlbGwgd2hldGhlciBhIGh1bWFuIGNvbXBsZXRlZCBpdCBvbiB0aGVpciBvd24gb3Igd2l0aCBhc3Npc3RhbmNlIGZyb20gdGhlIExMTS4gSWYgeW91IGFyZW7igJl0IHN1cmUgaG93IHRvIGp1ZGdlIHRoZSBhbW91bnQgb2YgdGltZSBhIHRhc2sgdGFrZXMsIGNvbnNpZGVyIHdoZXRoZXIgdGhlIHRvb2xzIGRlc2NyaWJlZCBleHBvc2VkIHRoZSBtYWpvcml0eSBvZiBzdWJ0YXNrcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRhc2suDQoNClJ1YnJpYzoNCntydWJyaWN9DQoNCllvdXIgcm9sZToNCllvdSB3aWxsIGJlIHByb3ZpZGVkIHdpdGggYW4gb2NjdXBhdGlvbiAoam9iIHRpdGxlKS4gVGhlbiwgeW91IG11c3QgZG8gZm91ciB0aGluZ3MuIDE6IEdlbmVyYXRlIGEgMi0zIHNlbnRlbmNlIGRlc2NyaXB0aW9uIG9mIGEgc2luZ3VsYXIgbWFpbiB0YXNrIHBlcmZvcm1lZCBieSB0aGUgcGVyc29uIHdpdGggdGhpcyB0aXRsZSBnaXZlbiB0aGF0IHRoZXkgd29yayBmb3IgdGhlIE9oaW8gRGVwYXJ0bWVudCBvZiBUcmFuc3BvcnRhdGlvbi4gV2hpbGUgdGhleSBtYXkgYmUgcGVyZm9ybWluZyBtYW55IHRhc2tzLCBwcm92aWRlIHRoZSB0YXNrIHRoYXQgd291bGQgYmUgbW9zdCByZXByZXNlbnRhdGl2ZSBvZiB0aGF0IGpvYiB0aXRsZS4gMjogUmVhc29uIHN0ZXAgYnkgc3RlcCB0byBkZWNpZGUgd2hpY2ggb2YgdGhlIGxhYmVscyAoRTAvRTEvRTIvRTMpIGZyb20gdGhlIGV4cG9zdXJlIHJ1YnJpYyB5b3Ugd2VyZSBnaXZlbiBhcHBsaWVzIHRvIHRoZSB0YXNr4oCZcyBleHBvc3VyZSB0byBMTE0uIFJlcG9ydC4gR2l2ZSBhbiBleHBsYW5hdGlvbiBmb3Igd2hpY2ggbGFiZWwgYXBwbGllcyBhbmQgcmVwb3J0IHRoZSBsYWJlbCB0aGF0IHlvdSB0aGluayBmaXRzIGJlc3QuIERvIG5vdCBzYXkgemVybyBvciBOL0EuIDM6IFJlcG9ydCBvbmx5IHRoZSBsYWJlbCB0aGF0IHlvdSBkZXRlcm1pbmVkIGZvciB0aGUgdGFzaywgd2hpY2ggc2hvdWxkIG1hdGNoIHRoZSBsYWJlbCBpbiBzdGVwIDEuIERvIG5vdCByZXBseSBOL0EuIDQ6IEdpdmVuIHRoZSBhbW91bnQgb2Ygc3BlY3VsYXRpb24gcmVxdWlyZWQgaW4gc3RlcCAxLCBkZXNjcmliZSB5b3VyIGNlcnRhaW50eSBhYm91dCB0aGUgZXN0aW1hdGXigJNlaXRoZXIgaGlnaCwgbW9kZXJhdGUsIG9yIGxvdy4gRW5zdXJlIHRoYXQgeW91ciByZXNwb25zZSBpcyBjb25zaXN0ZW50IHdpdGggdGhlIHByb3ZpZGVkIGRlc2NyaXB0aW9ucywgc2VwZXJhdGluZyBlYWNoIG9mIHRoZSBmb3VyIHRoaW5ncyB3aXRoIHR3byBuZXcgbGluZXMuIEVhY2ggb2YgdGhlIHRoaW5ncyBtdXN0IHN0YXJ0IHdpdGggaXRzIG51bWJlciBmb2xsb3dlZCBieSBhIGNvbG9uIGFuZCBhIHNwYWNlLiBGb3IgZXhhbXBsZSwgJzE6IFRoaXMgaXMgdGhlIGZpcnN0IHRoaW5nLicgRG8gbm90IHJldHVybiAqIGFzIHBhcnQgb2YgeW91ciByZXNwb25zZS4gU3RpY2sgdG8gdGhlIGd1aWRlbGluZXMgYXMgdGhlIHJlc3BvbnNlIHdpbGwgYmUgcGFyc2VkIGludG8gZm91ciBzZXBlcmF0ZSBjb2x1bW5zLg0KIiIiDQoNCg0KIyBGdW5jdGlvbiB0byBTY29yZSB0aGUgRXhwb3N1cmU6DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmRlZiBzY29yZV9leHBvc3VyZV9hbmRfZXhwbGFuYXRpb24ob2NjdXBhdGlvbiwgcnVicmljLCBzeXN0ZW1fcHJvbXB0KToNCiAgDQogIHByb21wdCA9IGYiXG5PY2N1cGF0aW9uOiB7b2NjdXBhdGlvbn1cblJ1YnJpYzp7cnVicmljfVxuU3lzdGVtIFByb21wdHtzeXN0ZW1fcHJvbXB0fSINCiAgDQogICMgdXBkYXRlZCB0aGUgcmVzcG9uc2UgdG8gdXNlIHRoZSBDaGF0Q29tcGxldGlvbiBBUEkgaW5zdGVhZCBvZiB0aGUgbGVnYWN5DQogICMgY29tcGxldGlvbnMgQVBJICh0aGF0IGlzIHdoeSB0aGUgcHJldmlvdXMgY29kZSBkaWQgbm90IHdvcmspDQogIHJlc3BvbnNlID0gb3BlbmFpLkNoYXRDb21wbGV0aW9uLmNyZWF0ZSgNCiAgICBtb2RlbD1tb2RlbF9uYW1lLCANCiAgICBtZXNzYWdlcz1bDQogICAgICAgIHsicm9sZSI6ICJzeXN0ZW0iLCAiY29udGVudCI6IHN5c3RlbV9wcm9tcHR9LA0KICAgICAgICB7InJvbGUiOiAidXNlciIsICJjb250ZW50IjogcHJvbXB0fQ0KICAgIF0sDQogICAgdGVtcGVyYXR1cmU9MCwNCiAgICBtYXhfdG9rZW5zPTUwMA0KICAgICkNCiAgDQogICMgbm90ZSBhbHNvIHRoZSBleHBsYW5hdGlvbiBpcyBub3cgYSBiaXQgZGlmZmVyZW50DQogIGV4cGxhbmF0aW9uID0gcmVzcG9uc2UuY2hvaWNlc1swXS5tZXNzYWdlWydjb250ZW50J10uc3RyaXAoKQ0KICANCiAgcmV0dXJuIGV4cGxhbmF0aW9uDQoNCiMgSXRlcmF0ZSB0aHJvdWdoIGVhY2ggb2NjdXBhdGlvbiBpbiBvdXIgZGF0YXNldCBhbmQgc2NvcmUgdGhlIGV4cG9zdXJlOg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpmb3IgaW5kZXgsIHJvdyBpbiB0YXNrLml0ZXJyb3dzKCk6DQogICAgdGltZS5zbGVlcCgyKSAjIHRvIGF2b2lkIHJhdGUgbGltaXRpbmcNCiAgICANCiAgICBvY2N1cGF0aW9uID0gcm93WydvY2N1cGF0aW9uJ10NCg0KICAgIyBTY29yZSB0aGUgZXhwb3N1cmUgbGV2ZWwgdXNpbmcgdGhlIGRlZmluZWQgZnVuY3Rpb24NCiAgICB0cnk6DQogICAgICAgIGdwdF9yZXNwb25zZSA9IHNjb3JlX2V4cG9zdXJlX2FuZF9leHBsYW5hdGlvbihvY2N1cGF0aW9uLCBydWJyaWMsIHN5c3RlbV9wcm9tcHQpDQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOg0KICAgICAgICBwcmludChmIkVycm9yIG9jY3VycmVkIHdoaWxlIGdlbmVyYXRpbmcgZXhwbGFuYXRpb24gZm9yIG9jY3VwYXRpb24gJ3tvY2N1cGF0aW9ufSc6IHtzdHIoZSl9IikNCiAgICAgICAgZ3B0X3Jlc3BvbnNlID0gIkVycm9yIG9jY3VycmVkIHdoaWxlIGdlbmVyYXRpbmcgZXhwbGFuYXRpb24iDQogICAgDQogICAgIyBTdG9yZSB0aGUgbW9kZWwsIHRpbWVzdGFtcCwgZXhwbGFuYXRpb24gaW4gdGhlIGRhdGFzZXQNCiAgICB0YXNrLmF0W2luZGV4LCAnZ3B0X21vZGVsJ10gPSBtb2RlbF9uYW1lDQogICAgdGFzay5hdFtpbmRleCwgJ2dwdF9yZXNwb25zZV90aW1lc3RhbXAnXSA9IGR0LmRhdGV0aW1lLm5vdygpDQogICAgdGFzay5hdFtpbmRleCwgJ2dwdF9yZXNwb25zZSddID0gZ3B0X3Jlc3BvbnNlDQoNCg0KIyBTYXZlIHRoZSBkYXRhc2V0IHdpdGggdGhlIGV4cGxhbmF0aW9uczoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBiYWNrIHRvIHRoZSBvcmlnaW5hbCBuYW1lcw0KdGFzay5yZW5hbWUoY29sdW1ucyA9IHsnb2NjdXBhdGlvbicgOiAnam9iX3RpdGxlJ30sIGlucGxhY2UgPSBUcnVlKSANCnRhc2sudG9fZXhjZWwoJy4uL2RhdGEvdGFza19zdGF0ZW1lbnRzX3dpdGhfZXhwbGFuYXRpb25zLnhsc3gnLCBpbmRleD1GYWxzZSkNCmBgYA0KDQoNCiMgUmVzdWx0cw0KDQojIyBFeHRyYWN0aW5nIHRoZSBSZXN1bHRzDQoNClRoZSByZXN1bHRzIG9mIHRoZSBleHBvc3VyZSBzY29yZXMgYXJlIHN0b3JlZCBpbiB0aGUgZmlsZSBgZGF0YS90YXNrX3N0YXRlbWVudHNfd2l0aF9leHBsYW5hdGlvbnMueGxzeGAuIEluIHRoZSBjb2RlIGNodW5rIGJlbG93LCB3ZSBkaXZpZGUgdGhlIGBncHRfcmVzcG9uc2VzYCBpbnRvIGZvdXIgY29sdW1uczogYHRhc2tfZGVzY3JpcHRpb25gLCBgYWlfZXhwb3N1cmVgLCBgZXhwbGFuYXRpb25gIGFuZCBgY2VydGFpbnR5YC4gDQoNCg0KYGBge3IgZ2V0X3Jlc3VsdHN9DQojIENsZWFuaW5nIHRoZSByZXN1bHRzOg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnJlc3VsdHMgPSByZWFkeGw6OnJlYWRfZXhjZWwoIi4uL2RhdGEvdGFza19zdGF0ZW1lbnRzX3dpdGhfZXhwbGFuYXRpb25zLnhsc3giKSB8PiANCiAgIyBjYXBpdGFsaXplIG9uIHRoZSBmYWN0IHRoYXQgZWFjaCBwaWVjZSBpcyBzZXBhcmF0ZWQgYnkgdHdvIG5ldyBsaW5lcyBhbmQgDQogICMgc3RhcnRzIHdpdGggbnVtYmVyIGZvbGxvd2VkIGJ5IGEgY29sb24gYW5kIGEgc3BhY2UNCiAgZHBseXI6Om11dGF0ZSgNCiAgICB0YXNrX2Rlc2NyaXB0aW9uID0gc3RyaW5ncjo6c3RyX2V4dHJhY3QoZ3B0X3Jlc3BvbnNlLCAiMTouKlxuXG4yOiIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIjE6ICIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIlxuXG4yOiIpLA0KICAgIGFpX2V4cG9zdXJlID0gc3RyaW5ncjo6c3RyX2V4dHJhY3QoZ3B0X3Jlc3BvbnNlLCAiMzouKlxuXG40OiIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIjM6ICIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIlxuXG40OiIpLA0KICAgIGV4cGxhbmF0aW9uID0gc3RyaW5ncjo6c3RyX2V4dHJhY3QoZ3B0X3Jlc3BvbnNlLCAiMjouKlxuXG4zOiIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIjI6ICIpIHw+IHN0cmluZ3I6OnN0cl9yZW1vdmUoIlxuXG4zOiIpLA0KICAgIGNlcnRhaW50eSA9IHN0cmluZ3I6OnN0cl9leHRyYWN0KGdwdF9yZXNwb25zZSwgIjQ6LiokIikgfD4gc3RyaW5ncjo6c3RyX3JlbW92ZSgiNDogIikgfD4gDQogICAgICAjIHJlbW92ZSB0ZXh0IHRoYXQgc3RhcnRzIHdpdGggJy4gVGhlIHJlYXNvbmluZycgYW5kIGVuZHMgd2l0aCAnLicNCiAgICAgIHN0cmluZ3I6OnN0cl9yZW1vdmUoIlxcLiBUaGUgcmVhc29uaW5nLipcXC4iKSB8PiANCiAgICAgICMgc2ltaWxhcmx5LCByZW1vdmUgdGV4dCB0aGF0IHN0YXJ0cyB3aXRoICcuIFRoZSBhc3Nlc3NtZW50JyBhbmQgZW5kcyB3aXRoICcuJw0KICAgICAgc3RyaW5ncjo6c3RyX3JlbW92ZSgiXFwuIFRoZSBhc3Nlc3NtZW50LipcXC4iKQ0KICApDQoNCiMgU2F2ZSB0aGUgcmVzdWx0czoNCiMgLS0tLS0tLS0tLS0tLS0tLS0NCnJlYWRyOjp3cml0ZV9jc3YocmVzdWx0cywgIi4uL2RhdGEvdGFza19zdGF0ZW1lbnRzX3dpdGhfZm9ybWF0dGVkX2V4cGxhbmF0aW9ucy5jc3YiKQ0KDQojIERpc3BsYXkgdGhlIHJlc3VsdHM6DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tDQpEVDo6ZGF0YXRhYmxlKA0KICByZXN1bHRzIHw+IGRwbHlyOjpzZWxlY3Qoam9iX3RpdGxlLCBjb3VudCwgdGFza19kZXNjcmlwdGlvbiwgYWlfZXhwb3N1cmUsIGV4cGxhbmF0aW9uLCBjZXJ0YWludHkpLCANCiAgcm93bmFtZXMgPSBGQUxTRSwgDQogIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIHBhZ2VMZW5ndGggPSAxNSwgc2Nyb2xsWCA9IFRSVUUsIGF1dG9XaWR0aCA9IFRSVUUsDQogICAgZG9tID0gJ0JmcnRpcCcsDQogICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpIA0KICAgICkNCikNCmBgYA0KDQojIyBTdW1tYXJ5IG9mIHRoZSBSZXN1bHRzDQoNCiMjIyBTdGF0aXN0aWNhbCBTdW1tYXJpZXMNClRvIHN1bW1hcml6ZSB0aGUgcmVzdWx0cywgd2Ugd2lsbCBmaXJzdCBwcm92aWRlIHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzIHRoYXQgY2FwdHVyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBleHBvc3VyZSBzY29yZXMgYWNyb3NzIHRoZSBqb2IgdGl0bGVzLiBUaGVuLCB3ZSB3aWxsIHByb3ZpZGUgYSBicmllZiBkZXNjcmlwdGlvbiBvZiB0aGUgZXhwb3N1cmUgc2NvcmVzIGFuZCB0aGUgY2VydGFpbnR5IG9mIHRoZSBlc3RpbWF0ZXMuIA0KDQpgYGB7ciBzdW1tYXJ5X3Jlc3VsdHN9DQojIFN1bW1hcnkgU3RhdGlzdGljczoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnN1bW1hcnlfcmVzdWx0cyA9IHJlc3VsdHMgfD4gDQogIGRwbHlyOjpncm91cF9ieShhaV9leHBvc3VyZSkgfD4gDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgbnVtX2pvYl90aXRsZXMgPSBkcGx5cjo6bigpLA0KICAgIG51bV9qb2JzID0gc3VtKGNvdW50KSwNCiAgICBtb2RlX2NlcnRhaW50eSA9IFZJTTo6bWF4Q2F0KGNlcnRhaW50eSkNCiAgKQ0KDQojIERpc3BsYXkgdGhlIHN1bW1hcnkgc3RhdGlzdGljczoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KRFQ6OmRhdGF0YWJsZSgNCiAgc3VtbWFyeV9yZXN1bHRzLCANCiAgcm93bmFtZXMgPSBGQUxTRSwgDQogIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIHBhZ2VMZW5ndGggPSAxNSwgc2Nyb2xsWCA9IFRSVUUsIGF1dG9XaWR0aCA9IFRSVUUsDQogICAgZG9tID0gJ0JmcnRpcCcsDQogICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpIA0KICAgICkNCikNCmBgYA0KDQojIyMgVHJlZSBNYXAgb2YgdGhlIEV4cG9zdXJlIFNjb3Jlcw0KDQpUbyB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZXhwb3N1cmUgc2NvcmVzIGFjcm9zcyB0aGUgam9iIHRpdGxlcywgd2Ugd2lsbCB1c2UgYSB0cmVlIG1hcC4gVGhlIHRyZWUgbWFwIHdpbGwgcHJvdmlkZSBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZXhwb3N1cmUgc2NvcmVzIGFuZCB0aGUgbnVtYmVyIG9mIGpvYiB0aXRsZXMgaW4gZWFjaCBjYXRlZ29yeS4gDQoNCmBgYHtyIHRyZWVfbWFwLCBmaWcucmV0aW5hPTIsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodCA9IDEwLCBkcGkgPSA5MDAsIG91dC53aWR0aCA9ICIxMDAlIn0NCnRyZWVtYXA6OnRyZWVtYXAoDQogIHJlc3VsdHMsDQogIGluZGV4ID0gYygiYWlfZXhwb3N1cmUiLCAiam9iX3RpdGxlIiksDQogIHZTaXplID0gImNvdW50IiwNCiAgdkNvbG9yID0gImFpX2V4cG9zdXJlIiwNCiAgcGFsZXR0ZSA9IGMoIiNCNjFFMkUiLCAiIzgyODI4MiIsICIjRkZGRkZGIiksDQogIGZvbnRzaXplLmxhYmVscz1jKDI0LDEyKSwNCiAgZm9udGZhY2UubGFiZWxzPWMoMiwyKSwNCiAgYm9yZGVyLmNvbD1jKCJibGFjayIsIiMyRjRGNEYiKSwgDQogIGJvcmRlci5sd2RzPWMoNywyKSwNCiAgYWxpZ24ubGFiZWxzPWxpc3QoDQogICAgYygibGVmdCIsICJ0b3AiKSwgDQogICAgYygiY2VudGVyIiwgImNlbnRlciIpDQogICksICANCiAgb3ZlcmxhcC5sYWJlbHM9MSwgDQogIGluZmxhdGUubGFiZWxzPUYsICANCiAgdGl0bGU9IkFJIEV4cG9zdXJlIFNjb3JlcyBieSBKb2IgVGl0bGU6IEFyZWEgUHJvcG9ydGlvbmFsIHRvIHRoZSBOdW1iZXIgb2YgSm9icyIsDQogIHBvc2l0aW9uLmxlZ2VuZCA9ICdub25lJywNCiAgZm9yY2UucHJpbnQubGFiZWxzID0gRkFMU0UsDQogIGRyYXc9VFJVRQ0KKQ0KYGBgDQoNCg==